using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text;
using System.Text.Json;

namespace STD.Shared.Infrastructure.Messaging;

/// <summary>
/// RabbitMQ消息总线实现
/// </summary>
public class RabbitMQMessageBus : IMessageBus, IDisposable
{
    private readonly IConnection _connection;
    private readonly IModel _channel;
    private readonly Dictionary<string, IModel> _consumerChannels;
    private readonly Dictionary<string, EventingBasicConsumer> _consumers;
    
    public RabbitMQMessageBus(IConnection connection)
    {
        _connection = connection;
        _channel = _connection.CreateModel();
        _consumerChannels = new Dictionary<string, IModel>();
        _consumers = new Dictionary<string, EventingBasicConsumer>();
    }
    
    /// <summary>
    /// 发布消息
    /// </summary>
    /// <typeparam name="T">消息类型</typeparam>
    /// <param name="message">消息</param>
    /// <param name="routingKey">路由键</param>
    /// <returns>是否成功</returns>
    public async Task<bool> PublishAsync<T>(T message, string routingKey)
    {
        try
        {
            var exchangeName = "microservice.exchange";
            _channel.ExchangeDeclare(exchangeName, ExchangeType.Topic, durable: true);
            
            var json = JsonSerializer.Serialize(message);
            var body = Encoding.UTF8.GetBytes(json);
            
            _channel.BasicPublish(
                exchange: exchangeName,
                routingKey: routingKey,
                basicProperties: null,
                body: body);
            
            return true;
        }
        catch
        {
            return false;
        }
    }
    
    /// <summary>
    /// 订阅消息
    /// </summary>
    /// <typeparam name="T">消息类型</typeparam>
    /// <param name="routingKey">路由键</param>
    /// <param name="handler">消息处理器</param>
    /// <returns>订阅ID</returns>
    public async Task<string> SubscribeAsync<T>(string routingKey, Func<T, Task> handler)
    {
        var exchangeName = "microservice.exchange";
        var queueName = $"queue.{routingKey}.{Guid.NewGuid()}";
        
        var channel = _connection.CreateModel();
        channel.ExchangeDeclare(exchangeName, ExchangeType.Topic, durable: true);
        channel.QueueDeclare(queueName, durable: true, exclusive: false, autoDelete: false);
        channel.QueueBind(queueName, exchangeName, routingKey);
        
        var consumer = new EventingBasicConsumer(channel);
        consumer.Received += async (model, ea) =>
        {
            try
            {
                var body = ea.Body.ToArray();
                var json = Encoding.UTF8.GetString(body);
                var message = JsonSerializer.Deserialize<T>(json);
                
                if (message != null)
                {
                    await handler(message);
                }
                
                channel.BasicAck(ea.DeliveryTag, false);
            }
            catch
            {
                channel.BasicNack(ea.DeliveryTag, false, true);
            }
        };
        
        channel.BasicConsume(queue: queueName, autoAck: false, consumer: consumer);
        
        var subscriptionId = Guid.NewGuid().ToString();
        _consumerChannels[subscriptionId] = channel;
        _consumers[subscriptionId] = consumer;
        
        return subscriptionId;
    }
    
    /// <summary>
    /// 取消订阅
    /// </summary>
    /// <param name="subscriptionId">订阅ID</param>
    /// <returns>是否成功</returns>
    public async Task<bool> UnsubscribeAsync(string subscriptionId)
    {
        try
        {
            if (_consumerChannels.TryGetValue(subscriptionId, out var channel))
            {
                channel.Close();
                _consumerChannels.Remove(subscriptionId);
                _consumers.Remove(subscriptionId);
                return true;
            }
            return false;
        }
        catch
        {
            return false;
        }
    }
    
    /// <summary>
    /// 发送消息到队列
    /// </summary>
    /// <typeparam name="T">消息类型</typeparam>
    /// <param name="message">消息</param>
    /// <param name="queueName">队列名称</param>
    /// <returns>是否成功</returns>
    public async Task<bool> SendToQueueAsync<T>(T message, string queueName)
    {
        try
        {
            _channel.QueueDeclare(queueName, durable: true, exclusive: false, autoDelete: false);
            
            var json = JsonSerializer.Serialize(message);
            var body = Encoding.UTF8.GetBytes(json);
            
            _channel.BasicPublish(
                exchange: "",
                routingKey: queueName,
                basicProperties: null,
                body: body);
            
            return true;
        }
        catch
        {
            return false;
        }
    }
    
    /// <summary>
    /// 从队列接收消息
    /// </summary>
    /// <typeparam name="T">消息类型</typeparam>
    /// <param name="queueName">队列名称</param>
    /// <param name="handler">消息处理器</param>
    /// <returns>订阅ID</returns>
    public async Task<string> ReceiveFromQueueAsync<T>(string queueName, Func<T, Task> handler)
    {
        var channel = _connection.CreateModel();
        channel.QueueDeclare(queueName, durable: true, exclusive: false, autoDelete: false);
        
        var consumer = new EventingBasicConsumer(channel);
        consumer.Received += async (model, ea) =>
        {
            try
            {
                var body = ea.Body.ToArray();
                var json = Encoding.UTF8.GetString(body);
                var message = JsonSerializer.Deserialize<T>(json);
                
                if (message != null)
                {
                    await handler(message);
                }
                
                channel.BasicAck(ea.DeliveryTag, false);
            }
            catch
            {
                channel.BasicNack(ea.DeliveryTag, false, true);
            }
        };
        
        channel.BasicConsume(queue: queueName, autoAck: false, consumer: consumer);
        
        var subscriptionId = Guid.NewGuid().ToString();
        _consumerChannels[subscriptionId] = channel;
        _consumers[subscriptionId] = consumer;
        
        return subscriptionId;
    }
    
    public void Dispose()
    {
        foreach (var channel in _consumerChannels.Values)
        {
            channel?.Close();
        }
        
        _channel?.Close();
        _connection?.Close();
    }
} 